home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (c) 1993, 1994
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that: (1) source code distributions
- * retain the above copyright notice and this paragraph in its entirety, (2)
- * distributions including binary code include the above copyright notice and
- * this paragraph in its entirety in the documentation or other materials
- * provided with the distribution, and (3) all advertising materials mentioning
- * features or use of this software display the following acknowledgement:
- * ``This product includes software developed by the University of California,
- * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
- * the University nor the names of its contributors may be used to endorse
- * or promote products derived from this software without specific prior
- * written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- */
- #ifndef lint
- static char rcsid[] =
- "@(#)$Header: pcap-file.c,v 1.00 94/06/20 19:07:56 leres Exp $ (LBL)";
- #endif
-
- /*
- * pcap-file.c - supports offline use of tcpdump
- * Extraction/creation by Jeffrey Mogul, DECWRL
- * Modified by Steve McCanne, LBL.
- *
- * Used to read saved packets
- * The first record in the file contains saved values for the machine
- * dependent values so we can print the dump file on any architecture.
- */
-
- #include <sys/types.h>
- #include <sys/time.h>
-
- #include <net/bpf.h>
-
- #include <errno.h>
- #include <memory.h>
- #include <stdio.h>
- #if __STDC__
- #include <stdlib.h>
- #endif
- #include <unistd.h>
-
- #include "pcap-int.h"
- #include "order.h"
-
-
- /*
- * We use the "receiver-makes-right" approach to byte order,
- * because time is at a premium when we are writing the file.
- * In other words, the pcap_file_header and pcap_pkthdr,
- * records are written in host byte order.
- * Note that the packets are always written in network byte order.
- *
- * ntoh[ls] aren't sufficient because we might need to swap on a big-endian
- * machine (if the file was written in little-end order).
- */
-
- #define ERROR_TRUNC 1
- #define ERROR_BADVERSION 2
- #define ERROR_BADF 3
- #define ERROR_EOF 4 /* not really an error, just a status */
-
- int link_type[] =
- { SNOOP_TYPE_OTHER, SNOOP_TYPE_ETHERNET, SNOOP_TYPE_ETHERNET,
- SNOOP_TYPE_OTHER, SNOOP_TYPE_OTHER, SNOOP_TYPE_OTHER,
- SNOOP_TYPE_8023, SNOOP_TYPE_OTHER, SNOOP_TYPE_OTHER,
- SNOOP_TYPE_OTHER, SNOOP_TYPE_FDDI };
- int snoop_type[SNOOP_TYPE_UNASSIGNED+1] =
- { DLT_IEEE802, DLT_IEEE802, DLT_IEEE802, DLT_IEEE802,
- DLT_EN10MB, DLT_NULL, DLT_NULL, DLT_NULL, DLT_FDDI, DLT_NULL, DLT_NULL };
- int snoop_size[SNOOP_TYPE_UNASSIGNED+1] =
- { DLS_IEEE802, DLS_IEEE802, DLS_IEEE802, DLS_IEEE802,
- DLS_EN10MB, 0, 0, 0, DLS_FDDI, 0, 0 };
-
- static int read_tcpdump_header(struct pcap_file_header *hp, FILE *fp,
- pcap_t *p, char *errbuf);
- static int next_tcpdump_packet(pcap_t *p, struct pcap_pkthdr *hdr,
- u_char *buf, int buflen);
- static void swap_tcpdump(struct pcap_file_header *hp);
- static int read_snoop_header(struct pcap_file_snoop_header *hp, FILE *fp,
- pcap_t *p, char *errbuf);
- static int next_snoop_packet(pcap_t *p, struct pcap_snoop_pkthdr *hdr,
- u_char *buf, int buflen);
- void swap_snoop(struct pcap_file_snoop_header *hp);
-
-
- pcap_t *
- pcap_open_offline(char *fname, int snaplen, char *errbuf)
- {
- register pcap_t *p;
- register FILE *fp;
- struct pcap_file_header tcpdump;
- struct pcap_file_snoop_header snoop;
- int linklen;
-
- p = (pcap_t *)malloc(sizeof(*p));
- if (p == NULL) {
- strcpy(errbuf, "out of swap");
- return NULL;
- }
-
- #ifdef notdef
- bzero(p, sizeof(*p));
- #else
- memset(p, 0, sizeof(*p));
- #endif
- /*
- * Set this field so we don't close stdin in pcap_close!
- */
- p->fd = -1;
-
- if (fname[0] == '-' && fname[1] == '\0')
- fp = stdin;
- else {
- fp = fopen(fname, "r");
- if (fp == NULL) {
- sprintf(errbuf, "%s: %s", fname, pcap_strerror(errno));
- goto bad;
- }
- }
- p->rf.rfile = fp;
-
- if (!read_tcpdump_header(&tcpdump, fp, p, errbuf))
- {
- p->rf.format = FORMAT_TCPDUMP;
- p->tzoff = tcpdump.thiszone;
- p->snapshot = tcpdump.snaplen;
- p->linktype = tcpdump.linktype;
- p->bufsize = tcpdump.snaplen;
- p->rf.version_major = tcpdump.version_major;
- p->rf.version_minor = tcpdump.version_minor;
- }
- else
- if (!read_snoop_header(&snoop, fp, p, errbuf))
- {
- p->rf.format = FORMAT_SNOOP2;
- p->tzoff = 0;
- p->snapshot = snaplen;
- p->linktype = snoop_type[snoop.linktype];
- p->bufsize = snoop_size[snoop.linktype]; /* assume worst case */
- p->rf.version_major = snoop.version;
- p->rf.version_minor = 0;
- if (p->linktype == DLT_NULL || p->bufsize == 0)
- {
- sprintf(errbuf, "unsupported snoop type");
- goto bad;
- }
- }
- else
- {
- sprintf(errbuf, "bad dump file format");
- goto bad;
- }
-
- /* Align link header as required for proper data alignment */
- linklen = 14; /* XXX */
- p->rf.base = (u_char *)malloc(p->bufsize + BPF_ALIGNMENT);
- p->buffer = p->rf.base + BPF_ALIGNMENT - (linklen % BPF_ALIGNMENT);
-
- return p;
- bad:
- free(p);
- return NULL;
- }
-
- /*
- * Print out packets stored in the opened file.
- * If cnt > 0, return after 'cnt' packets, otherwise continue until eof.
- */
- int
- pcap_read_offline(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
- {
- struct bpf_insn *fcode = p->fcode.bf_insns;
- int status = 0;
- int n = 0;
-
- while (status == 0) {
- struct pcap_pkthdr tcpdump;
- struct pcap_snoop_pkthdr snoop;
- struct pcap_hdr hdr;
-
- switch (p->rf.format)
- {
- case FORMAT_TCPDUMP:
- status = next_tcpdump_packet(p, &tcpdump, p->buffer, p->bufsize);
- hdr.ts = tcpdump.ts;
- hdr.caplen = tcpdump.caplen;
- hdr.len = tcpdump.len;
- hdr.drops = 0;
- break;
- case FORMAT_SNOOP2:
- status = next_snoop_packet(p, &snoop, p->buffer, p->bufsize);
- hdr.ts.tv_sec = snoop.secs;
- hdr.ts.tv_usec = snoop.usecs;
- hdr.len = snoop.len;
- hdr.caplen = snoop.caplen;
- hdr.drops = snoop.drops;
- break;
- }
- if (status > 0) /* in case of EOF */
- return 0;
- if (status) /* in case of some error */
- return -1;
-
- ++p->md.stat.ps_recv;
- if (p->rf.format == FORMAT_SNOOP2)
- p->md.stat.ps_drop = snoop.drops;
-
- if (hdr.caplen > p->snapshot)
- hdr.caplen = p->snapshot;
-
- if (fcode == NULL ||
- bpf_filter(fcode, p->buffer, hdr.len, hdr.caplen)) {
- (*callback)(user, &hdr, p->buffer);
- if (++n >= cnt && cnt > 0)
- break;
- }
- }
- /*XXX this breaks semantics tcpslice expects */
- return n;
- }
-
- static int
- read_tcpdump_header(struct pcap_file_header *hp, FILE *fp, pcap_t *p,
- char *errbuf)
- {
- rewind(fp);
- if (fread((char *)hp, sizeof(*hp), 1, fp) != 1) {
- sprintf(errbuf, "fread: %s", pcap_strerror(errno));
- return -1;
- }
- if (hp->magic != TCPDUMP_MAGIC) {
- if (SWAPLONG(hp->magic) != TCPDUMP_MAGIC) {
- sprintf(errbuf, "bad dump file format");
- return -1;
- }
- p->rf.swapped = 1;
- swap_tcpdump(hp);
- }
- if (hp->version_major < PCAP_VERSION_MAJOR) {
- sprintf(errbuf, "archaic file format");
- return -1;
- }
- return 0;
- }
-
- /*
- * Return the next packet. Return the header in hdr
- * and the contents in buf. Return 0 on success, ERROR_EOF if there were
- * no more packets, and ERROR_TRUNC if a partial packet was encountered.
- */
- static int
- next_tcpdump_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char *buf, int buflen)
- {
- FILE *fp = p->rf.rfile;
-
- /* read the stamp */
- if (fread((char *)hdr, sizeof(*hdr), 1, fp) != 1) {
- /* probably an EOF, though could be a truncated packet */
- return 1;
- }
-
- if (p->rf.swapped) {
- /* these were written in opposite byte order */
- hdr->caplen = SWAPLONG(hdr->caplen);
- hdr->len = SWAPLONG(hdr->len);
- hdr->ts.tv_sec = SWAPLONG(hdr->ts.tv_sec);
- hdr->ts.tv_usec = SWAPLONG(hdr->ts.tv_usec);
- }
- /*
- * We interchanged the caplen and len fields at version 2.3,
- * in order to match the bpf header layout. But unfortunately
- * some files were written with version 2.3 in their headers
- * but without the interchanged fields.
- */
- if (p->rf.version_minor < 3 ||
- (p->rf.version_minor == 3 && hdr->caplen > hdr->len)) {
- int t = hdr->caplen;
- hdr->caplen = hdr->len;
- hdr->len = t;
- }
-
- if (hdr->caplen > buflen) {
- sprintf(p->errbuf, "bad dump file format");
- return -1;
- }
-
- /* read the packet itself */
-
- if (fread((char *)buf, hdr->caplen, 1, fp) != 1) {
- sprintf(p->errbuf, "truncated dump file");
- return -1;
- }
- return 0;
- }
-
- static void
- swap_tcpdump(struct pcap_file_header *hp)
- {
- hp->version_major = SWAPSHORT(hp->version_major);
- hp->version_minor = SWAPSHORT(hp->version_minor);
- hp->thiszone = SWAPLONG(hp->thiszone);
- hp->sigfigs = SWAPLONG(hp->sigfigs);
- hp->snaplen = SWAPLONG(hp->snaplen);
- hp->linktype = SWAPLONG(hp->linktype);
- }
-
- static int read_snoop_header(struct pcap_file_snoop_header *hp, FILE *fp,
- pcap_t *p, char *errbuf)
- {
- rewind(fp);
- if (fread((char *)hp, sizeof(*hp), 1, fp) != 1) {
- sprintf(errbuf, "fread: %s", pcap_strerror(errno));
- return -1;
- }
- if (memcmp(hp->id, SNOOP_MAGIC, sizeof(hp->id))) {
- sprintf(errbuf, "bad dump file format");
- return -1;
- }
- if (byteorder() != BIG_ENDIAN) {
- p->rf.swapped = 1;
- swap_snoop(hp);
- }
- if (hp->version < PCAP_SNOOP_VERSION) {
- sprintf(errbuf, "archaic file format");
- return -1;
- }
- return 0;
- }
-
- static int next_snoop_packet(pcap_t *p, struct pcap_snoop_pkthdr *hdr,
- u_char *buf, int buflen)
- {
- FILE *fp = p->rf.rfile;
-
- /* read the stamp */
- if (fread((char *)hdr, sizeof(*hdr), 1, fp) != 1) {
- /* probably an EOF, though could be a truncated packet */
- return 1;
- }
-
- if (p->rf.swapped) {
- /* these were written in opposite byte order */
- hdr->len = SWAPLONG(hdr->len);
- hdr->caplen = SWAPLONG(hdr->caplen);
- hdr->totlen = SWAPLONG(hdr->totlen);
- hdr->drops = SWAPLONG(hdr->drops);
- hdr->secs = SWAPLONG(hdr->secs);
- hdr->usecs = SWAPLONG(hdr->usecs);
- }
-
- if (hdr->caplen > buflen) {
- sprintf(p->errbuf, "bad dump file format");
- return -1;
- }
-
- /* read the packet itself */
-
- if (fread((char *)buf, hdr->caplen, 1, fp) != 1) {
- sprintf(p->errbuf, "truncated dump file");
- return -1;
- }
-
- if (fseek(fp, hdr->totlen-hdr->caplen-sizeof(*hdr), SEEK_CUR) != 0) {
- return 1;
- }
-
- return 0;
- }
-
- void
- swap_snoop(struct pcap_file_snoop_header *hp)
- {
- hp->version = SWAPLONG(hp->version);
- hp->linktype = SWAPLONG(hp->linktype);
- }
-
- int
- pcap_setfilter_offline(pcap_t * p, struct bpf_program *fp)
- {
- p->fcode = *fp;
- return 0;
- }
-
- int
- pcap_stats_offline(pcap_t * p, struct pcap_stat *ps)
- {
- ps->ps_drop = p->md.stat.ps_drop;
- ps->ps_recv = p->md.stat.ps_recv;
- ps->ps_ifdrop = 0;
- return 0;
- }
-